package com.apexcode.judge.service.impl;

import cn.hutool.core.date.StopWatch;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import com.apexcode.common.core.constants.Constants;
import com.apexcode.common.core.enums.CodeRunStatus;
import com.apexcode.common.core.constants.JudgeConstants;
import com.apexcode.judge.callback.DockerStartResultCallback;
import com.apexcode.judge.callback.StatisticsCallback;
import com.apexcode.judge.config.DockerSandBoxPool;
import com.apexcode.judge.domain.CompileResult;
import com.apexcode.judge.domain.SandBoxExecuteResult;
import com.apexcode.judge.service.ISandboxPoolService;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.ExecCreateCmdResponse;
import com.github.dockerjava.api.command.StatsCmd;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class SandboxPoolServiceImpl implements ISandboxPoolService {

    @Autowired
    private DockerSandBoxPool sandBoxPool; // Docker沙箱池，用于管理容器

    @Autowired
    private DockerClient dockerClient; // Docker客户端，用于执行Docker命令

    private String containerId; // 当前使用的容器ID

    private String userCodeFileName; // 用户代码文件路径

    @Value("${sandbox.limit.time:5}")
    private Long timeLimit; // 代码执行时间限制，默认5秒

    /**
     * 执行Java代码的主方法
     * @param userId 用户ID
     * @param userCode 用户提交的Java代码
     * @param inputList 输入用例列表
     * @return SandBoxExecuteResult 执行结果
     */
    @Override
    public SandBoxExecuteResult exeJavaCode(Long userId, String userCode, List<String> inputList) {
        containerId = sandBoxPool.getContainer(); // 从池中获取一个容器
        createUserCodeFile(userCode); // 创建用户代码文件
        // 编译代码
        CompileResult compileResult = compileCodeByDocker();
        if (!compileResult.isCompiled()) {
            sandBoxPool.returnContainer(containerId); // 编译失败，归还容器
            deleteUserCodeFile(); // 删除代码文件
            return SandBoxExecuteResult.fail(CodeRunStatus.COMPILE_FAILED, compileResult.getExeMessage());
        }
        // 执行代码
        return executeJavaCodeByDocker(inputList);
    }

    /**
     * 创建用户代码文件
     * @param userCode 用户提交的代码内容
     */
    private void createUserCodeFile(String userCode) {
        String codeDir = sandBoxPool.getCodeDir(containerId); // 获取容器对应的代码目录
        log.info("user-pool路径信息：{}", codeDir);
        userCodeFileName = codeDir + File.separator + JudgeConstants.USER_CODE_JAVA_CLASS_NAME;
        // 如果文件已存在，先删除旧文件
        if (FileUtil.exist(userCodeFileName)) {
            FileUtil.del(userCodeFileName);
        }
        FileUtil.writeString(userCode, userCodeFileName, Constants.UTF8); // 写入代码到文件
    }

    /**
     * 使用Docker编译用户代码
     * @return CompileResult 编译结果
     */
    private CompileResult compileCodeByDocker() {
        String cmdId = createExecCmd(JudgeConstants.DOCKER_JAVAC_CMD, null, containerId); // 创建编译命令
        DockerStartResultCallback resultCallback = new DockerStartResultCallback();
        CompileResult compileResult = new CompileResult();
        try {
            dockerClient.execStartCmd(cmdId)
                    .exec(resultCallback)
                    .awaitCompletion(); // 执行编译并等待完成
            if (CodeRunStatus.FAILED.equals(resultCallback.getCodeRunStatus())) {
                compileResult.setCompiled(false);
                compileResult.setExeMessage(resultCallback.getErrorMessage()); // 设置错误信息
            } else {
                compileResult.setCompiled(true);
            }
            return compileResult;
        } catch (InterruptedException e) {
            throw new RuntimeException(e); // 异常抛出，交由上层处理
        }
    }

    /**
     * 使用Docker执行Java代码
     * @param inputList 输入用例列表
     * @return SandBoxExecuteResult 执行结果
     */
    private SandBoxExecuteResult executeJavaCodeByDocker(List<String> inputList) {
        List<String> outList = new ArrayList<>(); // 记录输出结果
        long maxMemory = 0L;  // 最大占用内存
        long maxUseTime = 0L; // 最大运行时间
        // 对每个输入用例执行代码
        for (String inputArgs : inputList) {
            String cmdId = createExecCmd(JudgeConstants.DOCKER_JAVA_EXEC_CMD, inputArgs, containerId);
            StopWatch stopWatch = new StopWatch(); // 计时器
            StatsCmd statsCmd = dockerClient.statsCmd(containerId); // 启动资源监控
            StatisticsCallback statisticsCallback = statsCmd.exec(new StatisticsCallback());
            stopWatch.start(); // 开始计时
            DockerStartResultCallback resultCallback = new DockerStartResultCallback();
            try {
                dockerClient.execStartCmd(cmdId)
                        .exec(resultCallback)
                        .awaitCompletion(timeLimit, TimeUnit.SECONDS); // 执行代码并设置时间限制
                if (CodeRunStatus.FAILED.equals(resultCallback.getCodeRunStatus())) {
                    return SandBoxExecuteResult.fail(CodeRunStatus.NOT_ALL_PASSED); // 未通过所有用例
                }
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            stopWatch.stop(); // 结束计时
            statsCmd.close(); // 关闭监控
            long userTime = stopWatch.getLastTaskTimeMillis(); // 获取本次执行耗时
            maxUseTime = Math.max(userTime, maxUseTime); // 更新最大耗时
            Long memory = statisticsCallback.getMaxMemory();
            if (memory != null) {
                maxMemory = Math.max(maxMemory, memory); // 更新最大内存
            }
            outList.add(resultCallback.getMessage().trim()); // 记录输出
        }
        sandBoxPool.returnContainer(containerId); // 归还容器
        deleteUserCodeFile(); // 清理文件

        return getSanBoxResult(inputList, outList, maxMemory, maxUseTime); // 返回封装结果
    }

    /**
     * 创建Docker执行命令
     * @param javaCmdArr 基础命令数组
     * @param inputArgs 输入参数
     * @param containerId 容器ID
     * @return String 命令ID
     */
    private String createExecCmd(String[] javaCmdArr, String inputArgs, String containerId) {
        if (!StrUtil.isEmpty(inputArgs)) {
            String[] inputArray = inputArgs.split(" "); // 分割输入参数
            javaCmdArr = ArrayUtil.append(JudgeConstants.DOCKER_JAVA_EXEC_CMD, inputArray); // 拼接命令
        }
        ExecCreateCmdResponse cmdResponse = dockerClient.execCreateCmd(containerId)
                .withCmd(javaCmdArr)
                .withAttachStderr(true)
                .withAttachStdin(true)
                .withAttachStdout(true)
                .exec();
        return cmdResponse.getId();
    }

    /**
     * 封装沙箱执行结果
     * @param inputList 输入用例列表
     * @param outList 输出结果列表
     * @param maxMemory 最大内存占用
     * @param maxUseTime 最大执行时间
     * @return SandBoxExecuteResult 执行结果对象
     */
    private SandBoxExecuteResult getSanBoxResult(List<String> inputList, List<String> outList,
                                                 long maxMemory, long maxUseTime) {
        if (inputList.size() != outList.size()) {
            return SandBoxExecuteResult.fail(CodeRunStatus.NOT_ALL_PASSED, outList, maxMemory, maxUseTime);
        }
        return SandBoxExecuteResult.success(CodeRunStatus.SUCCEED, outList, maxMemory, maxUseTime);
    }

    /**
     * 删除用户代码文件
     */
    private void deleteUserCodeFile() {
        FileUtil.del(userCodeFileName);
    }
}